﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Noris.Schedule.Support;
using Noris.Schedule.Support.Sql;
using Noris.Tools.FrxEditor.Components;
using Noris.Tools.FrxEditor.SqlCommands;
using Noris.Schedule.Support.Components;
using Noris.Tools.FrxEditor.Pics;
using Noris.Schedule.Support.Parser;

namespace Noris.Tools.FrxEditor.Data
{
    /// <summary>
    /// Formulář pro zadání dat ReportData
    /// </summary>
    public partial class ReportData_Form : DForm
    {
        #region Konstrukce, Inicializace
        /// <summary>
        /// Konstruktor
        /// </summary>
        public ReportData_Form()
        {
            InitializeComponent();
            this.InitData();
        }
        private void InitData()
        {
            this.DataColumnsInit();
            this.DataParametersInit();
        }
        #endregion
        #region Konfigurace okna
        /// <summary>
        /// Uživatelská konfigurace okna. Obsahuje Layout a další data (poslední stav okna).
        /// </summary>
        protected override int[] ConfigUserData
        {
            set
            {   // Provádí se při otevírání okna:
                if (this.ConfigPersistent == ConfigPersistentMode.Layout && value.Length > 0)
                    this.SplitAttributes.SplitterPosition = value[0];
                if (this.ConfigPersistent == ConfigPersistentMode.Layout && value.Length > 1)
                    this.ClassTree.SplitterPosition = value[1];
                if (this.ConfigPersistent == ConfigPersistentMode.Layout && value.Length > 2)
                    this.SplitParameters.SplitterPosition = value[2];
                //if (value.Length > 1)
                //    this._VariableListIsCategorized = (value[1] == 2);
                //if (this.ConfigPersistent == ConfigPersistentMode.Layout && value.Length > 2)
                //    this._Split.SplitterDistance = value[2];

            }
            get
            {   // Provádí se při zavření okna:
                List<int> data = new List<int>();
                data.Add(this.SplitAttributes.SplitterPosition);
                data.Add(this.ClassTree.SplitterPosition);
                data.Add(this.SplitParameters.SplitterPosition);
                
                //data.Add(this._FunctionListIsCategorized ? 2 : 1);
                //data.Add(this._VariableListIsCategorized ? 2 : 1);
                //data.Add(this._Split.SplitterDistance);

                //RectangleF test = this.Bounds;
                //Noris.Tools.FrxEditor.Components.Register.Set("Pokus", "Hodnota", test);

                return data.ToArray();
            }
        }
        #endregion
        #region Public property
        /// <summary>
        /// Datový objekt, jehož vlastnosti se zde editují.
        /// Setování vede k zobrazení dat objektu.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ReportData ReportData
        {
            get { return this._ReportData; }
            set { this._ReportDataShow(value); }
        }
        private ReportData _ReportData;
        #endregion
        #region Vazba na datový objekt (Show / Save)
        /// <summary>
        /// Panel OK vyžaduje uložení dat
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PanelOk_Save(object sender, EventArgs e)
        {
            this._ReportDataSave();
        }
        /// <summary>
        /// Provede se po vložení datového objektu: promítne jeho data do okna
        /// </summary>
        /// <param name="reportData"></param>
        private void _ReportDataShow(ReportData reportData)
        {
            this._ReportData = reportData;
            
            this.DataColumnsLoad(reportData);
            this.DataParametersLoad(reportData);

            this.PanelOk.IsDirty = false;
        }
        /// <summary>
        /// Uloží data z formuláře do objektu this._ReportData.
        /// </summary>
        private void _ReportDataSave()
        {
            ReportData reportData = this._ReportData;
            if (reportData == null)
            {
                Noris.Schedule.Support.Core.Dialog.ShowError("Ve formuláři není uložen objekt třídy ReportData, obsahující data reportu.");
                return;
            }

            this.DataColumnsSave(reportData);
            this.DataParametersSave(reportData);

            this.PanelOk.IsDirty = false;
        }
        #endregion
        #region Obsluha stránky 1 - strom tříd, vybrané sloupce
        /// <summary>
        /// Inicializace gridu Sloupce
        /// </summary>
        private void DataColumnsInit()
        {
            this.ClassTree.RelationToLeftEnabled = false; // true;
            this.ClassTree.ButtonImage = IconLibrary22.ArrowRightDouble_FromFile;
            this.ClassTree.AttributeSelect += new ClassTreePanelHandler(ClassTree_AttributeSelect);
            this.DataColumnsGrid.DataTable = ReportDataColumnCollection.CreateEditDataTable();
            this.DataColumnsGrid.CellContentDoubleClick += new DataGridViewCellEventHandler(DataColumnsGrid_CellContentDoubleClick);
            this.DataColumnsGrid.CellDoubleClick += new DataGridViewCellEventHandler(DataColumnsGrid_CellContentDoubleClick);
            this.DataColumnsGrid.ValueChanged += new SimpleDataValueChangeHandler(DataColumnsGrid_ValueChanged);
            this.DataColumnsFindButton.Image = IconLibrary22.UnindentTask_FromFile;
            this.DataColumnsDeleteButton.Image = IconLibrary22.EntryDelete_FromFile;
        }
        /// <summary>
        /// Po změně dat v gridu parametrů
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void DataColumnsGrid_ValueChanged(object sender, SimpleDataValueChangeArgs args)
        {
            this.PanelOk.IsDirty = true;
        }
        /// <summary>
        /// Provede se po vložení datového objektu: promítne jeho data do okna, strana 1: sloupce
        /// </summary>
        /// <param name="reportData"></param>
        private void DataColumnsLoad(ReportData reportData)
        {
            this.ClassTree.RootClassNumber = reportData.RootClassNumber;
            this.SqlEditor.EditedText = reportData.DataQuery;
            this.DataColumnsGrid.DataTable.Rows.Import(reportData.Columns.EditDataTable.Rows, false);
            if (this.DataColumnsGrid.Rows.Count > 0)
                this.DataColumnsGrid.CurrentCell = this.DataColumnsGrid.Rows[0].Cells[3];
        }
        /// <summary>
        /// Uloží data z formuláře do objektu ReportData, strana 1: sloupce
        /// </summary>
        private void DataColumnsSave(ReportData reportData)
        {
            reportData.Columns.EditDataTable = this.DataColumnsGrid.DataTable;
        }
        /// <summary>
        /// Po výběru sady atributů v okně Strom tříd + Atributy
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        private void ClassTree_AttributeSelect(object sender, Schedule.Support.Components.ClassTreePanelArgs args)
        {
            this.DataColumnsGridAddAttributes(args);
        }
        /// <summary>
        /// Událost DoubleClick na obsahu buňky
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataColumnsGrid_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
        {
            this.DataColumnsGridFindAttributes();
        }
        /// <summary>
        /// Po kliknutí na tlačítko "Najdi řádky DataColumnsGrid ve stromu tříd"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataColumns_FindButtonClick(object sender, EventArgs e)
        {
            this.DataColumnsGridFindAttributes();
        }
        /// <summary>
        /// Po kliknutí na tlačítko "Smaž vybrané řádky DataColumnsGrid"
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DataColumns_DeleteButtonClick(object sender, EventArgs e)
        {
            this.DataColumnsGridDeleteRows();
        }
        /// <summary>
        /// Do tabulky sloupců (this.DataColumnsGrid) přidá další řádky odpovídající vybraným atributům
        /// </summary>
        /// <param name="args"></param>
        private void DataColumnsGridAddAttributes(Schedule.Support.Components.ClassTreePanelArgs args)
        {
            SimpleDataRow row;
            foreach (ClassTreeAttributeItem attItem in args.SelectedAttributes)
            {
                string alias = ReportDataColumnCollection.CreateColumnAlias(attItem.Column, this.DataColumnsGrid.DataTable);
                string className = null;
                string editStyle = null;
                string dataTypeName = (attItem.NorisAttributes != null ? attItem.NorisAttributes.Typ : "");
                ReportColumnDataType dataType = ReportData.ConvertParamType(dataTypeName);
                dataTypeName = ReportData.ConvertParamType(dataType);
                switch (args.SelectedClassNode.NodeType)
                {
                    case ClassTreeNode.ClassTreeNodeType.Class:
                        className = args.SelectedClassNode.ClassInfo.NazevTridy;
                        if (attItem.NorisAttributes != null)
                            editStyle = attItem.NorisAttributes.EditacniStyl;
                        break;
                    case ClassTreeNode.ClassTreeNodeType.Relation:   // Pro Node typu Relation se nabízejí jen reference, název, číslo subjektu a výraz:
                        if (attItem.NorisAttributes != null)
                        className = args.SelectedClassNode.RelationInfo.Nazev;
                        editStyle = "";
                        break;
                }
                row = this.DataColumnsGrid.DataTable.Rows.Add(args.SelectedClassNode.FullTreePath, attItem.FullTreePath, className, attItem.Name, editStyle, alias, dataTypeName);
                row.Cells[2].ToolTipText = args.SelectedClassNode.ToolTipText;
                row.Cells[3].ToolTipText = attItem.ToolTipText;
            }
            this.PanelOk.IsDirty = true;
        }
        /// <summary>
        /// Z gridu this.DataColumnsGrid načte řádky, které jsou vybrané (jakýkoli Cell je Selected), 
        /// a tyto atributy se pokusí dohledat ve stromu tříd + seznamu atributů.
        /// </summary>
        private void DataColumnsGridFindAttributes()
        {
            IEnumerable<SimpleDataGridRow> rows = this.DataColumnsGrid.SelectedSimpleGridRowsFromCell;

            List<string> keys = new List<string>();
            foreach (SimpleDataGridRow row in rows)
            {
                string key = row.DataRow.Get<string>("column_id");
                if (!String.IsNullOrEmpty(key))
                    keys.Add(key);
            }

            if (keys.Count > 0)
                this.ClassTree.SelectAttributes(keys);
        }
        /// <summary>
        /// Smaže označené řádky
        /// </summary>
        private void DataColumnsGridDeleteRows()
        {
            IEnumerable<SimpleDataRow> rows = this.DataColumnsGrid.SelectedSimpleDataRowsFromCell;
            this.DataColumnsGrid.DataTable.Rows.Remove(rows);
            this.PanelOk.IsDirty = true;
        }
        #endregion
        #region Obsluha stránky 2 - možnost editace, SQL dotaz, parametry
        /// <summary>
        /// Iniciace stránky 2
        /// </summary>
        private void DataParametersInit()
        {
            this.SqlEditor.ParserSetting = ParserSqlExpressionSetting.SqlExpressionSetting;
            this.ParamGrid.DataTable = ReportDataParamCollection.CreateEditDataTable();
            this.ParamGrid.ValueChanged += new SimpleDataValueChangeHandler(ParamGrid_ValueChanged);
        }
        /// <summary>
        /// Po změně dat v gridu parametrů
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void ParamGrid_ValueChanged(object sender, SimpleDataValueChangeArgs args)
        {
            this.PanelOk.IsDirty = true;
        }
        /// <summary>
        /// Provede se po vložení datového objektu: promítne jeho data do okna, strana 2: SQL příkaz a parametry
        /// </summary>
        /// <param name="reportData"></param>
        private void DataParametersLoad(ReportData reportData)
        {
            this.SqlCmdEditEnable.Checked = reportData.DataQueryEditEnabled;
            this.ParamGrid.DataTable.Rows.Import(reportData.Parameters.EditDataTable.Rows, false);      // Nejprve naplním do Gridu parametry z ReportData
            this.SqlEditor.EditedText = reportData.DataQuery;                                           // Vložením textu se vyvolá event SqlEditor_EditedTextChanged, kde se do Gridu přidají parametry z SQL textu
        }
        /// <summary>
        /// Uloží data z formuláře do objektu ReportData, strana 2: SQL příkaz a parametry
        /// </summary>
        private void DataParametersSave(ReportData reportData)
        {
            reportData.DataQuery = this.SqlEditor.EditedText;
            reportData.DataQueryEditEnabled = this.SqlCmdEditEnable.Checked;
            reportData.Parameters.EditDataTable = this.ParamGrid.DataTable;
        }
        /// <summary>
        /// Po změně hodnoty zaškrtávátka "Možnost editace SQL dotazu
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SqlCmdEditEnable_CheckedChanged(object sender, EventArgs e)
        {
            bool editEnabled = this.SqlCmdEditEnable.Checked;
            this.SqlEditor.EditorEnabled = (editEnabled);
            this.SplitAttributes.Panel1Collapsed = editEnabled;
            this.DataColumnsFindButton.Visible = !editEnabled;
            this.DataColumnsDeleteButton.Visible = !editEnabled;

            this.PanelOk.IsDirty = true;
        }
        /// <summary>
        /// Po změně textu v SQL editoru
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SqlEditor_EditedTextChanged(object sender, EventArgs e)
        {
            this.PanelOk.IsDirty = true;
            this._ParamGridFillFromSql(this.SqlEditor.EditedText);
        }
        /// <summary>
        /// V zadaném SQL příkazu najde parametry a zajistí jejich zobrazení v gridu
        /// </summary>
        /// <param name="sql"></param>
        private void _ParamGridFillFromSql(string sql)
        {
            List<string> parameters = Data.ReportData.FindParameterNames(sql);
            string imprint = this._CreateParamImprint(parameters);
            if (String.Equals(this._LastParamImprint, imprint))
                return;
            this._LastParamImprint = imprint;

            // Provedu synchronizaci položek vizuální tabulky s parametry z SQL příkazu, podle jména parametru.
            // Výsledkem bude soupis položek z obou stran, které nemají ekvivalent na druhé straně:
            List<SimpleDataRow> notFindRows = new List<SimpleDataRow>();
            List<string> notFindNames = new List<string>();
            List<string> tableNames = new List<string>();        // Soupis jmen v tabulce existujících
            foreach (SimpleDataRow row in this.ParamGrid.DataTable.Rows)
            {
                string paramName = row.Get<string>("param_name");
                if (!parameters.Exists(s => String.Equals(s, paramName, StringComparison.InvariantCultureIgnoreCase)))
                    notFindRows.Add(row);
                else
                    tableNames.Add(paramName);
            }
            foreach (string name in parameters)
            {
                if (!tableNames.Exists(s => String.Equals(s, name, StringComparison.InvariantCultureIgnoreCase)))
                    notFindNames.Add(name);
            }

            // Nyní mám v notFindRows ty řádky tabulky, jejichž jméno parametru není uvedeno v SQL příkazu,
            // a v notFindNames mám ty jména z SQL příkazu, která nejsou v tabulce.
            // Pokud je na obou stranách jedna položka, změním v tabulce jméno a je hotovo (uživatel právě mění jméno parameru).
            // Jinak provedu brutální náhradu (delete & add).
            if (notFindRows.Count == 1 && notFindNames.Count == 1)
            {   // Jeden řádek tabulky se neshoduje s jedním názvem parametru SQL příkazu: změním v něm název.
                notFindRows[0]["param_name"] = notFindNames[0];
            }
            else
            {   // Víz rozdílů: řádky z tabulky odeberu a přidám nové:
                foreach (SimpleDataRow row in notFindRows)
                    this.ParamGrid.DataTable.Rows.Remove(row);
                foreach (string name in notFindNames)
                    this.ParamGrid.DataTable.Rows.Add(name, "", "nově zadaný");
            }
            this.ParamGrid.Refresh();
        }
        /// <summary>
        /// Vrátí otisk soupisu parametrů. Obsahuje pouze setříděná jména, ToLower.
        /// </summary>
        /// <param name="parameterNames"></param>
        /// <returns></returns>
        private string _CreateParamImprint(List<string> parameterNames)
        {
            if (parameterNames.Count > 1)
                parameterNames.Sort();

            string imprint = "";
            foreach (string name in parameterNames)
                imprint += (imprint.Length == 0 ? "" : ";") + name;

            return imprint;
        }
        private string _LastParamImprint;
        #endregion

        private void button1_Click(object sender, EventArgs e)
        {
            if (this._ReportData == null) return;

            string sqlText = this.SqlEditor.EditedText;
            
            sqlText = this._ReportData.Parameters.ApplyParamToCommand(sqlText, ReportParamValue.Edit);

            IEnumerable<ISqlCommand> commands = SqlCommand.Parse(sqlText);
            SqlSelect select = commands.FirstOrDefault(cmd => cmd.SqlCommand == "SELECT") as SqlSelect;
            if (select != null)
            {
                select.PartTop.Text = "TOP 3 ";
                sqlText = select.Text;
            }

            Steward.ExecuteInTryCatch(
                delegate 
                {
                    this.DataGrid.DataTable = SimpleDataTable.Load(sqlText);
                }, "Zadaný SQL příkaz obsahuje chybu: %0");
        }

    }
}
